//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#include "precomp.h"
#include "rilversion.h"

#ifdef SIEMENS_MAGNETO
#include <simtkit.h>
#endif

extern LPCTSTR g_tszRegKeyIntrinsycRILBand;
extern LPCTSTR g_tszRegKeyIntrinsycRILHandover;
extern DWORD g_dwRadioAudioProfilesVersion;

#ifdef RIL_FAKECSQ
extern volatile BOOL g_fFakeSignalStrength;
#endif


#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
char gszGetSignalQualityLast[128];
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH


BOOL  g_bCINDIsActive = FALSE;
UINT  g_iLastRSSI = 99;
UINT  g_iLastSignal = 99;
DWORD g_dwCurrentPhoneBook = RIL_PBLOC_UNKNOWN;
char g_szSequenceFromDialer[MAX_ATCMD_LEN] = "";

// For GCF 27.18.3 we need small cache for the names of the FD phonebook entries, since the MMI
// doesn't offer them to be edited.
#define MAX_FDPB_CACHE  5
WCHAR g_wszFDPBCache[MAX_FDPB_CACHE][MAXLENGTH_PHONEBOOKTEXT];

extern BOOL g_bRadioOff;

//
// Bit error rates
//
static const DWORD g_rgdwBitErrorRates[] =
{
    14,     // 0
    28,     // 1
    57,     // 2
    113,    // 3
    226,    // 4
    453,    // 5
    905,    // 6
    1810,   // 7
};
#define NUM_BITERRORRATES     (sizeof(g_rgdwBitErrorRates) / sizeof(DWORD))


static const SUPPORTED_MODEM g_SupportedModems[] = {
    { "MC45",   MODEMTYPE_SIEMENS_MC45},        // only for testing purposes
    { "MC46",   MODEMTYPE_SIEMENS_MC46},        // only for testing purposes
    { "MC55",   MODEMTYPE_SIEMENS_MC55},
    { "MC56",   MODEMTYPE_SIEMENS_MC56},
    { "XT55",   MODEMTYPE_SIEMENS_MC55},
    { "XT56",   MODEMTYPE_SIEMENS_MC56},
    { "AC43",   MODEMTYPE_SIEMENS_AC45},        // only for testing purposes
    { "AC45",   MODEMTYPE_SIEMENS_AC45},
    { "TC63",   MODEMTYPE_SIEMENS_TC63},
    { "MC75",   MODEMTYPE_SIEMENS_MC75},
    { NULL, MODEMTYPE_UNKNOWN}
};


//
// Restricted SIM commands
//
static const DWORD g_rgdwRestrictedSIMCmds[] =
{
    -1,     // 0
    176,    // RIL_SIMCMD_READBINARY
    178,    // RIL_SIMCMD_READRECORD
    192,    // RIL_SIMCMD_GETRESPONSE
    214,    // RIL_SIMCMD_UPDATEBINARY
    220,    // RIL_SIMCMD_UPDATERECORD
    242,    // RIL_SIMCMD_STATUS
};
#define NUM_RESTRICTEDSIMCMDS   (sizeof(g_rgdwRestrictedSIMCmds) / sizeof(DWORD))

#if defined (IOCTL_RIL_GETANTENNASTATUS)
// Antenna Status
static const DWORD g_rgdwAntennaStatus[] =
{
    RIL_ANTENNASTATUS_CONNECTED,     // 0
    RIL_ANTENNASTATUS_SHORTEDGROUND,        // 1
    RIL_ANTENNASTATUS_SHORTED,            // 2
    RIL_ANTENNASTATUS_DISCONNECTED,            // 3
};
#define NUM_ANTENNASTATUS      (sizeof(g_rgdwAntennaStatus) / sizeof(DWORD))
#endif




extern RHA_FUNCTION_LIST RHAFunctionList;

//
// Some retry options definitions
//
#define PHONEBOOKOPTIONS_RETRIES        8
#define PHONEBOOKOPTIONS_RETRIES_DELAY  5000


void ClearFDPBCache()
{
    memset(g_wszFDPBCache, 0, MAX_FDPB_CACHE*MAXLENGTH_PHONEBOOKTEXT*sizeof(WCHAR));
}
//
//
//
DWORD GetMuxVersion(void) {
DWORD              dwVersion = -1;

#ifndef DUAL_PORT_SERIAL

BYTE               VerInfo[512];
VS_FIXEDFILEINFO*  pFixedInfo;
UINT               uiLen;

  if (GetFileVersionInfo(TEXT("multiplx.dll"), 0, 512, VerInfo)) {
    if (VerQueryValue(VerInfo, TEXT("\\"), (LPVOID*)(&pFixedInfo), &uiLen)) {
      dwVersion = 0;
      dwVersion |= ((BYTE)pFixedInfo->dwProductVersionLS);
      dwVersion |= ((BYTE)(pFixedInfo->dwProductVersionLS >> 16) << 8);
      dwVersion |= ((BYTE)pFixedInfo->dwProductVersionMS) << 16;
      dwVersion |= ((BYTE)(pFixedInfo->dwProductVersionMS >> 16)) << 24;
    }
  }

#endif // DUAL_PORT_SERIAL

  return dwVersion;
}

//
//
//
void TraceDriverInfo(void) {
#ifdef SIEMENS_MAGNETO
    RILRetailTrace((TEXT("RilDrv: Current RIL Version: %d.%d.%d.%d (Magneto-Build)\r\n"), (RIL_VERSION_NUM>>24)&255, (RIL_VERSION_NUM>>16)&255, (RIL_VERSION_NUM>>8)&255, RIL_VERSION_NUM&255));
#else // SIEMENS_MAGNETO
    RILRetailTrace((TEXT("RilDrv: Current RIL Version: %d.%d.%d.%d (Ozone-Build)\r\n"), (RIL_VERSION_NUM>>24)&255, (RIL_VERSION_NUM>>16)&255, (RIL_VERSION_NUM>>8)&255, RIL_VERSION_NUM&255));
#endif // SIEMENS_MAGNETO
#ifdef DUAL_PORT_SERIAL
    RILRetailTrace((TEXT("RilDrv: Dual-Port Version\r\n")));
#else // DUAL_PORT_SERIAL
    // Get Mux driver information
    RILRetailTrace((TEXT("RilDrv: Mux Port (%i Ports)\r\n"), NUMBER_OF_PORTS));

	//DWORD dwVersion = GetMuxVersion();
    //if (dwVersion != -1) {
    //  RILRetailTrace((TEXT("RilDrv: Current Multiplexer Version: %d.%d.%d.%d\r\n"), (dwVersion>>24)&255, (dwVersion>>16)&255, (dwVersion>>8)&255, dwVersion&255));
    //} else {
    //  RILRetailTrace((TEXT("RilDrv: Unable to get mux version\r\n")));
    //}
#endif // DUAL_PORT_SERIAL
}

//
//
//
static HRESULT ParseGetEquipmentInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetEquipmentInfo);
    RILEQUIPMENTINFO* prei = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prei = (RILEQUIPMENTINFO*)AllocBlob(sizeof(RILEQUIPMENTINFO));
    if (!prei) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prei, 0x00, sizeof(RILEQUIPMENTINFO));
    prei->cbSize = sizeof(RILEQUIPMENTINFO);

    // Parse "<prefix><manufacturer_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szManufacturer, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MANUFACTURER;

    // Parse "<prefix><model_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                ||
        !ParseUnquotedString(szRsp, '\r', prei->szModel, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MODEL;

    // Parse "<prefix><revision_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                   ||
        !ParseUnquotedString(szRsp, '\r', prei->szRevision, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_REVISION;


    // Parse "<prefix><serial_number><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szSerialNumber, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_SERIALNUMBER;

    pBlob = (void*)prei;
    cbBlob = sizeof(RILEQUIPMENTINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prei);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetEquipmentInfo(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetEquipmentInfo);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CGMI;+CGMM;+CGMR;+CGSN\r", CMDOPT_NONE, APIID_GETEQUIPMENTINFO,
                  ParseGetEquipmentInfo, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetEquipmentState(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetEquipmentState);
    RILEQUIPMENTSTATE * pres = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pres = (RILEQUIPMENTSTATE*)AllocBlob(sizeof(RILEQUIPMENTSTATE));
    if (!pres) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    g_PowerManager.FillEquipmentStateStruct(pres, g_PowerManager.GetEquipmentState());

    pBlob = (void*)pres;
    cbBlob = pres->cbSize;

Error:
    if (FAILED(hr)) {
        FreeBlob(pres);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetEquipmentState(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetEquipmentState);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_IGNORERADIOOFF|CMDOPT_NOOP, APIID_GETEQUIPMENTSTATE, ParseGetEquipmentState, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetEquipmentState(DWORD dwParam, DWORD dwEquipmentState)
{
  TBD_FUNCTION(RILDrv_SetEquipmentState);
  HRESULT hr;

  CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

  if (!pHandle) {
    return E_INVALIDARG;
  }

  switch (dwEquipmentState) {
    case RIL_EQSTATE_MINIMUM:
    case RIL_EQSTATE_DISABLETXANDRX:
    case RIL_EQSTATE_FULL:
      if (!g_PowerManager.EnterEquipmentState(pHandle, dwEquipmentState)) {
        return E_FAIL;
      }
      hr = S_OK;
      QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
      return hr;
    case RIL_EQSTATE_DISABLETX:
    case RIL_EQSTATE_DISABLERX:
      return E_NOTIMPL;
  }
  return E_INVALIDARG;
}


//
//
//
HRESULT ParseGetPhonebookOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetPhonebookOptions);
    UINT i;
    UINT nValue;
    char szLocation[MAX_ATCMD_LEN];
    RILPHONEBOOKINFO* prpbi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prpbi = (RILPHONEBOOKINFO*)AllocBlob(sizeof(RILPHONEBOOKINFO));
    if (!prpbi) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prpbi, 0x00, sizeof(RILPHONEBOOKINFO));
    prpbi->cbSize = sizeof(RILPHONEBOOKINFO);

    // Parse "<prefix>+CPBS: <storage>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CPBS: ", szRsp) ||
        !ParseString(szRsp, szLocation, MAX_ATCMD_LEN, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_PBLOCS; i++) {
        if (!strcmp(szLocation, g_rgszPBLocations[i])) {
            prpbi->dwStoreLocation = i;
            break;
        }
    }
    if (NUM_PBLOCS == i) {
        // We couldn't match the response with anything
        prpbi->dwStoreLocation = RIL_PBLOC_UNKNOWN;
    }
    g_dwCurrentPhoneBook = prpbi->dwStoreLocation;
    prpbi->dwParams |= RIL_PARAM_PBI_STORELOCATION;

    // Parse ","
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse "<used>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        prpbi->dwUsed = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_USED;

        // Parse ",<total>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }

        prpbi->dwTotal = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_TOTAL;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prpbi;
    cbBlob = sizeof(RILPHONEBOOKINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prpbi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetPhonebookOptions(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetPhonebookOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmdWithRetry(COMMAND_PORT, pHandle, "AT+CPBS?\r", CMDOPT_NONE, APIID_GETPHONEBOOKOPTIONS, ParseGetPhonebookOptions, NULL, hr, PHONEBOOKOPTIONS_RETRIES, PHONEBOOKOPTIONS_RETRIES_DELAY)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetPhonebookOptions(DWORD dwParam, const RILPHONEBOOKINFO* lpPhonebookInfo)
{
    TBD_FUNCTION(RILDrv_SetPhonebookOptions);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Store location must be specified
    if (!(lpPhonebookInfo->dwParams & RIL_PARAM_PBI_STORELOCATION)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Store location index must be in the valid range
    TBD_ASSERT(NUM_PBLOCS > lpPhonebookInfo->dwStoreLocation);
    if (RIL_PBLOC_UNKNOWN == lpPhonebookInfo->dwStoreLocation) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Siemens modules do not support the phonebook type "EN" (emergency numbers).
    if (lpPhonebookInfo->dwStoreLocation == RIL_PBLOC_SIMEMERGENCY) {
        hr = E_INVALIDARG;
        goto Error;
    }

    g_dwCurrentPhoneBook = lpPhonebookInfo->dwStoreLocation;
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKSTORAGECHANGED, lpPhonebookInfo->dwStoreLocation)) {
        delete pnd;
        pnd = NULL;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[lpPhonebookInfo->dwStoreLocation]);
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}

//
//
//
static HRESULT ParseArrayOfPhonebookEntries(LPCSTR szCmd, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    TBD_FUNCTION(ParseArrayOfPhonebookEntries);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    WCHAR wszText[MAXLENGTH_PHONEBOOKTEXT];
    RILPHONEBOOKENTRY* rgrbpe = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, szCmd, szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrbpe, sizeof(RILPHONEBOOKENTRY), nUsed, &nAllocated, MISC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrbpe[nUsed], 0x00, sizeof(RILPHONEBOOKENTRY));
        rgrbpe[nUsed].cbSize = sizeof(RILPHONEBOOKENTRY);

        // Parse "<index>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwIndex = nValue;
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_INDEX;

        // Parse ",<number>,<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
            !MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
            !StringToRILAddress(szAddress, (BYTE)nValue, rgrbpe[nUsed].raAddress)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_ADDRESS;

        // Parse ",<text>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseQuotedEncodedString(ENCODING_UCS2, szRsp, wszText, wszText + MAXLENGTH_PHONEBOOKTEXT)) {
            goto Continue;
        }
        (void)wcsncpyz(rgrbpe[nUsed].wszText, wszText, MAXLENGTH_PHONEBOOKTEXT);
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_TEXT;

        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && rgrbpe[nUsed].dwIndex < MAX_FDPB_CACHE)
        {
            wcscpy(g_wszFDPBCache[rgrbpe[nUsed].dwIndex], wszText);
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrbpe;
    cbBlob = nUsed * sizeof(RILPHONEBOOKENTRY);

Error:
    if (FAILED(hr)) {
        delete[] (BYTE*)rgrbpe;
    }
    return hr;
}


//
//
//
static HRESULT ParseReadPhonebookEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseReadPhonebookEntries);
    return ParseArrayOfPhonebookEntries("+CPBR: ", szRsp, pBlob, cbBlob);
}


//
//
//
HRESULT RILDrv_ReadPhonebookEntries(DWORD dwParam, DWORD dwStartIndex, DWORD dwEndIndex)
{
    TBD_FUNCTION(RILDrv_ReadPhonebookEntries);
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBR=%u,%u\r", dwStartIndex, dwEndIndex);
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_READPHONEBOOKENTRIES, ParseReadPhonebookEntries, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_WritePhonebookEntry(DWORD dwParam, const RILPHONEBOOKENTRY* lpEntry)
{
    TBD_FUNCTION(RILDrv_WritePhonebookEntry);
    CNotificationData* pnd = NULL;
    BYTE bTypeOfAddress;
    char szAddress[MAXLENGTH_ADDRESS];
    char szCmd[MISC_CMDBUF_LENGTH_PB];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpEntry->dwParams & RIL_PARAM_PBE_INDEX) || !(lpEntry->dwParams & RIL_PARAM_PBE_ADDRESS)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(lpEntry->raAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }

    if (RIL_PBINDEX_FIRSTAVAILABLE == lpEntry->dwIndex) {
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=,\"%s\",%u", szAddress, bTypeOfAddress);
    } else {
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=%u,\"%s\",%u", lpEntry->dwIndex, szAddress,
                         bTypeOfAddress);
    }
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    // Add text, if supplied
    if (lpEntry->dwParams & RIL_PARAM_PBE_TEXT) {
        (void)strncpyz(szWalk, ",", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        char szEncodedText[MAX_ATCMD_LEN];

        UCS2ToGSMHex((WCHAR *)lpEntry->wszText, wcslen(lpEntry->wszText), szEncodedText, sizeof(szEncodedText));
        (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)strncpyz(szWalk, szEncodedText, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && lpEntry->dwIndex < MAX_FDPB_CACHE && lpEntry->dwIndex != RIL_PBINDEX_FIRSTAVAILABLE)
        {
            wcscpy(g_wszFDPBCache[lpEntry->dwIndex], lpEntry->wszText);
        }
    }
    else
    {
        if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && lpEntry->dwIndex < MAX_FDPB_CACHE && lpEntry->dwIndex != RIL_PBINDEX_FIRSTAVAILABLE)
        {
            (void)strncpyz(szWalk, ",", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);

            char szEncodedText[MAX_ATCMD_LEN];

            UCS2ToGSMHex((WCHAR *)g_wszFDPBCache[lpEntry->dwIndex], wcslen(g_wszFDPBCache[lpEntry->dwIndex]), szEncodedText, sizeof(szEncodedText));
            (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            (void)strncpyz(szWalk, szEncodedText, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            (void)strncpyz(szWalk, "\"", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYSTORED, lpEntry->dwIndex)) {
        delete pnd;
        pnd = NULL;
    }
    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_WRITEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_DeletePhonebookEntry(DWORD dwParam, DWORD dwIndex)
{
    TBD_FUNCTION(RILDrv_DeletePhonebookEntry);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYDELETED, dwIndex)) {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPBW=%u\r", dwIndex);

    if (g_dwCurrentPhoneBook == RIL_PBLOC_SIMFIXDIALING && dwIndex < MAX_FDPB_CACHE)
        g_wszFDPBCache[dwIndex][0] = 0;

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_DELETEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}


#ifdef RIL_SUPPORT_CSIM
//
//
//
static HRESULT ParseSendSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSendSimCmd);
    UINT i;
    UINT nValue;
    UINT cbResponse;
    BYTE* pbResponse = NULL;
    BYTE* pbResponseWalk;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSIM: <length>,"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CSIM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    TBD_ASSERT(0 == nValue % 2);

    // Allocate the storage
    cbResponse = nValue / 2;
    pbResponse = (BYTE *)AllocBlob(cbResponse);
    if (!pbResponse) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    pbResponseWalk = pbResponse;
    for (i = 0; i < cbResponse; i++) {
        if (!*szRsp || !*(szRsp + 1)) {
            hr = E_FAIL;
            goto Error;
        }
        *pbResponseWalk = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
        pbResponseWalk++;
        szRsp += 2;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)pbResponse;
    cbBlob = cbResponse;

Error:
    if (FAILED(hr)) {
        FreeBlob(pbResponse);
    }
    return hr;
}
#endif

//
//
//
HRESULT RILDrv_SendSimCmd(DWORD dwParam, const BYTE* lpbCommand, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SendSimCmd);

#ifdef RIL_SUPPORT_CSIM
    // In future versions of the MC75 it might be, that AT+CSIM support is added.
    TBD_ASSERT(NULL != lpbCommand);
    TBD_ASSERT(0 != dwSize);

    LPSTR szCmd = NULL;
    char szPrefix[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (!IS_ADI) {
        hr = E_NOTIMPL;
        goto Error;
    }

    // Add "AT+CSIM=<length>," to prefix string
    (void)_snprintfz(szPrefix, MAX_ATCMD_LEN, "AT+CSIM=%u,", dwSize * 2);

    // Add "<prefix>\"<command>\"<CR>"
    // NOTE: we take ownerswhip of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbCommand, dwSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_SENDSIMCMD, ParseSendSimCmd, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
#else
    HRESULT hr = E_NOTIMPL;
#endif
    return hr;
}


//
//
//
static HRESULT ParseSendRestrictedSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseSendRestrictedSimCmd);
    UINT i;
    UINT nSW1;
    UINT nSW2;
    LPSTR szResponseString = NULL;
    UINT cbResponseString;
    UINT cbStruct;
    BYTE* pbResponseWalk;
    LPSTR pchResponseStringWalk;
    RILSIMRESPONSE* prsr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        // No response data present
        cbStruct = sizeof(RILSIMRESPONSE);
    } else {
        // Parse "<response>"
        // NOTE: we take ownerswhip of allocated szResponseString
        if (!ParseUnlimitedUnquotedString(szRsp, '\r', szResponseString, cbResponseString, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        TBD_ASSERT(0 == (cbResponseString - 1) % 2);
        cbStruct = sizeof(RILSIMRESPONSE) + (cbResponseString - 1) / 2;
    }

    // The MC75 cannot provide the "file not found" (148,4) message. Instead it will report "SIM not accesed" (111,0).
    if (IS_ADI && nSW1 == 111 && nSW2 == 0)
    {
        nSW1 = 148;
        nSW2 = 4;
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);

    // Decode the reponse data, if present
    if (sizeof(RILSIMRESPONSE) < cbStruct) {
        pbResponseWalk = prsr->pbResponse;
        pchResponseStringWalk = szResponseString;
        for (i = 0; i < cbStruct - sizeof(RILSIMRESPONSE); i++) {
            *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
            pbResponseWalk++;
            pchResponseStringWalk += 2;
        }
        prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    }

    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        FreeBlob(prsr);
    }
    delete[] szResponseString;
    return hr;
}


//
//
//
HRESULT RILDrv_SendRestrictedSimCmd(DWORD dwParam, DWORD dwCommand, const RILSIMCMDPARAMETERS* lpParameters,
                                    const BYTE* lpbData, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SendRestrictedSimCmd);

    TBD_ASSERT(0 != dwCommand);
    TBD_ASSERT(NUM_RESTRICTEDSIMCMDS > dwCommand);

    LPSTR szCmd = NULL;
    char szPrefix[MAX_ATCMD_LEN];
    LPSTR szPrefixWalk = szPrefix;
    UINT cbDataSizeToUse = 0;
    COM_PORT_TYPE iPort;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+CRSM=<command>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), "AT+CRSM=%u", g_rgdwRestrictedSIMCmds[dwCommand]);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szPrefixWalk);

    if (lpParameters && (lpParameters->dwParams & RIL_PARAM_SCP_FILEID)) {
        // Add ",<fileid>" to prefix string
        (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u", lpParameters->dwFileID);
        szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szPrefixWalk);

        if ((lpParameters->dwParams & RIL_PARAM_SCP_PARAM1) &&
            (lpParameters->dwParams & RIL_PARAM_SCP_PARAM2) &&
            (lpParameters->dwParams & RIL_PARAM_SCP_PARAM3)) {
            // Add ",<p1>,<p2>,<p3>" to prefix string
            (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                             lpParameters->dwParameter1, lpParameters->dwParameter2, lpParameters->dwParameter3);

            // If there's data, add a comma before that data
            if (dwSize)
            {
                strcat(szPrefixWalk, ",");
                cbDataSizeToUse = dwSize;
            }
        }
    }

    // Add "<prefix>\"<data>\"<CR>"
    // NOTE: we take ownerswhip of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbData, cbDataSizeToUse, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    iPort = COMMAND_PORT;
    if (IS_ADI)
        iPort = pHandle->GetDevice()->GetFreeAlternativeCmdPort(TRUE);

    if (!QueueCmd(iPort, pHandle, szCmd, CMDOPT_NONE|CMDOPT_CPIN2NEUTRAL, APIID_SENDRESTRICTEDSIMCMD, ParseSendRestrictedSimCmd, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
    return hr;
}


//
//
//
static HRESULT ParseGetSimRecordStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetSimRecordStatus);
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    RILSIMRECORDSTATUS *prsrs = NULL;
    UINT cbStruct = sizeof(RILSIMRECORDSTATUS);
    DWORD dwFileID;
    DWORD dwTotalSize;

    pBlob = NULL;
    cbBlob = 0;

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;
    hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp, pParam);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Now we need to munge this to be a RILSIMRECORDSTATUS structure instead
    prsrs = (RILSIMRECORDSTATUS*)AllocBlob(cbStruct);
    if (!prsrs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsrs, 0x00, cbStruct);

    // Was there an error of some sort?
    TBD_ASSERT((prsr->dwParams & RIL_PARAM_SR_STATUSWORD1) && (prsr->dwParams & RIL_PARAM_SR_STATUSWORD2));
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

    // The response has to be present and at least 7 bytes long
    if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (prsr->cbSize - sizeof(RILSIMRESPONSE) < 7))
    {
        hr = E_FAIL;
        goto Error;
    }

    // The file ID should be in bytes 5 and 6 -- in either case, we need at least 7 bytes
    dwFileID = (prsr->pbResponse[4] << 8) | prsr->pbResponse[5];
    prsrs->cbSize = cbStruct;
    prsrs->dwParams = (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_RECORDTYPE);
    prsrs->dwRecordType = RIL_SIMRECORDTYPE_UNKNOWN;
    if (IsElementarySimFile(dwFileID))
    {
        // Need at least 14 bytes for an elementary file
        if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 14)
        {
            hr = E_FAIL;
            goto Error;
        }

        // Byte 14 tells us the file type
        switch (prsr->pbResponse[13])
        {
            case 0x00:
            case 0x02: // This is what OEM1 gives us, but it's not in 11.11!
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
                break;
            case 0x01:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
                break;
            case 0x03:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
                break;
            default:
                break;
        }

        // Set more fields based on the file type
        dwTotalSize = (prsr->pbResponse[2] << 8) | prsr->pbResponse[3];
        if (prsrs->dwRecordType == RIL_SIMRECORDTYPE_TRANSPARENT)
        {
            // Size is bytes 3 and 4
            prsrs->dwParams |= RIL_PARAM_SRS_SIZE;
            prsrs->dwSize = dwTotalSize;
        }
        else if (prsrs->dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN)
        {
            // Make sure we have a fifteenth byte in this case
            if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 15)
            {
                hr = E_FAIL;
                goto Error;
            }

            prsrs->dwParams |= (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_ITEMCOUNT);
            prsrs->dwSize = prsr->pbResponse[14];
            prsrs->dwItemCount = dwTotalSize / prsrs->dwSize;
        }
    }
    else
    {
        // We only set the file type, which is determined by byte 7
        switch (prsr->pbResponse[6])
        {
            case 0x01:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_MASTER;
                break;
            case 0x02:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_DEDICATED;
                break;
            default:
                break;
        }
    }

    pBlob = (void*)prsrs;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        FreeBlob(prsrs);
    }
    FreeBlob(prsr);
    return hr;
}

//
//
//
HRESULT RILDrv_GetSimRecordStatus(DWORD dwParam, DWORD dwFileID)
{
    TBD_FUNCTION(RILDrv_GetSimRecordStatus);

    DWORD dwParameter1, dwParameter2, dwParameter3;
    DWORD dwRestrictedSIMCmd;
    WORD wFileID = (WORD) (dwFileID & 0xffff);
    COM_PORT_TYPE iPort;
    LPBYTE lpbData = NULL;
    DWORD dwDataSize = 0;
    LPSTR szCmd = NULL;
    char szPrefix[MAX_ATCMD_LEN];
    LPSTR szPrefixWalk = szPrefix;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    dwParameter1 = 0x00;
    dwParameter2 = 0x00;

    // Is this an elementary file?
    if (IsElementarySimFile(dwFileID))
    {
        // This is an elementary file -- send GET RESPONSE
        dwParameter3 = 15;  // min required length of response for EF
    }
    else
    {
        // This is not an elementary file
        dwParameter3 = 22;  // min required length of response for DF/MF
    }

    dwRestrictedSIMCmd = g_rgdwRestrictedSIMCmds[RIL_SIMCMD_GETRESPONSE];

    // Add "AT+CRSM=<command>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), "AT+CRSM=%u", dwRestrictedSIMCmd);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szPrefixWalk);

    // Add ",<fileid>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u", dwFileID);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szPrefixWalk);

    // Add ",<p1>,<p2>,<p3>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_ATCMD_LEN - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                     dwParameter1, dwParameter2, dwParameter3);

    // If there's data, add a comma before that data
    if (dwDataSize)
    {
        strcat(szPrefixWalk, ",");
    }

    // Add "<prefix>\"<data>\"<CR>"
    // NOTE: we take ownerswhip of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbData, dwDataSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    iPort = COMMAND_PORT;
    if (IS_ADI)
        iPort = pHandle->GetDevice()->GetFreeAlternativeCmdPort(TRUE);

    if (!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE|CMDOPT_CPIN2NEUTRAL, APIID_GETSIMRECORDSTATUS, ParseGetSimRecordStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
    return hr;
}


//
//
//
HRESULT RILDrv_GetSimToolkitProfile(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetSimToolkitProfile);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->GetProfile(pHandle);
    }
    return E_FAIL;
}


//
//
//
HRESULT RILDrv_SetSimToolkitProfile(DWORD dwParam, const BYTE* lpbProfile, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SetSimToolkitProfile);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SetProfile(pHandle);
    }
    return E_FAIL;
}


//
//
//
HRESULT RILDrv_TerminateSimToolkitSession(DWORD dwParam, DWORD dwCause)
{
    TBD_FUNCTION(RILDrv_TerminateSimToolkitSession);
    HRESULT hr = E_NOTIMPL;
#ifdef SIEMENS_MAGNETO
    RILSIMTOOLKITRSP Rsp;
    Rsp.dwParams = RIL_PARAM_SIMTKIT_RSP_TYPE | RIL_PARAM_SIMTKIT_RSP_RESPONSE;
    Rsp.dwResponse = 0;
    Rsp.dwType = SIM_NOTIFY_ENDSESSION;
    hr = RILDrv_SendSimToolkitCmdResponse(dwParam, &Rsp, 0, 0);
#else
    BYTE bCommand = 254;
    hr = RILDrv_SendSimToolkitEnvelopeCmd(dwParam, &bCommand, 1);
#endif
    return hr;
}


#ifdef SIEMENS_MAGNETO
//
//
//
HRESULT RILDrv_SendSimToolkitCmdResponse(DWORD dwParam, const RILSIMTOOLKITRSP* pRsp, LPBYTE pDetails, const DWORD dwDetailSize)
{
    TBD_FUNCTION(RILDrv_SendSimToolkitCmdResponse);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SendCmdResponse(pHandle, pRsp, pDetails, dwDetailSize);
    }
    return E_FAIL;
}


//
//
//
HRESULT RILDrv_SendSimToolkitEventDownload(DWORD dwParam, const DWORD dwEvent, LPBYTE pData, const DWORD dwDataSize)
{
    TBD_FUNCTION(RILDrv_SendSimToolkitEventDownload);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SendEventDownload(pHandle, dwEvent, pData, dwDataSize);
    }
    return E_FAIL;
}

#else // SIEMENS_MAGNETO

//
//
//
HRESULT RILDrv_SendSimToolkitCmdResponse(DWORD dwParam, const BYTE* lpbResponse, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SendSimToolkitCmdResponse);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SendCmdResponse(pHandle, lpbResponse, dwSize);
    }
    return E_FAIL;
}


//
//
//
HRESULT RILDrv_SendSimToolkitEnvelopeCmd(DWORD dwParam, const BYTE* lpbCommand, DWORD dwSize)
{
    TBD_FUNCTION(RILDrv_SendSimToolkitEnvelopeCmd);
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (pHandle) {
      return pHandle->GetDevice()->GetSimToolkit()->SendEnvelopeCmd(pHandle, lpbCommand, dwSize);
    }
    return E_FAIL;
}
#endif // SIEMENS_MAGNETO


//
//
//
static HRESULT ParseGetCostInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCostInfo);
    UINT nValue;
    RILCOSTINFO* prci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prci = (RILCOSTINFO*)AllocBlob(sizeof(RILCOSTINFO));
    if (!prci) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prci, 0x00, sizeof(RILCOSTINFO));
    prci->cbSize = sizeof(RILCOSTINFO);

    // Parse "<prefix>+CAOC: \"<ccm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAOC: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwCCM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_CCM;

    // Parse "<prefix>+CACM: \"<acm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CACM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_ACM;

    // Parse "<prefix>+CAMM: \"<acm_max>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAMM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwMaxACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_MAXACM;

    // Parse "<prefix>+CPUC: <currency>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPUC: ", szRsp) ||
        !ParseQuotedEncodedString(ENCODING_UCS2, szRsp, prci->wszCurrency, prci->wszCurrency + MAXLENGTH_CURRENCY)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_CURRENCY;

    // Parse "\"<ppu>\"<postfix>"
    if (!MatchStringBeginning(szRsp, ",\"", szRsp)      ||
        !ParseFixedPoint(szRsp, prci->dwCostPerUnit, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)       ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_COSTPERUNIT;

    pBlob = (void*)prci;
    cbBlob = sizeof(RILCOSTINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prci);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCostInfo(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetCostInfo);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // MC4x and MC5x modules don't give the proper response to AT+CAOC=0. Since AT+CAOC is always equal 0, the write command is redundant
    if (!QueueCmd(COMMAND_PORT, pHandle, "AT+CAOC;+CACM?;+CAMM?;+CPUC?\r", CMDOPT_NONE|CMDOPT_CPIN2RELATED, APIID_GETCOSTINFO, ParseGetCostInfo, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

const CME_TRANSLATION g_CMET_CPUC[] = {{RIL_E_INVALIDINDEX,RIL_E_INCORRECTPASSWORD},{NULL,NULL}};

//
//
//
HRESULT RILDrv_SetCostInfo(DWORD dwParam, const RILCOSTINFO* lpCostInfo, LPCSTR lpszPassword)
{
    TBD_FUNCTION(RILDrv_SetCostInfo);
    TBD_ASSERT(NULL != lpCostInfo);

    char szCmd[MISC_CMDBUF_LENGTH_COST];
    LPSTR szWalk = szCmd;
    char szPrice[MAX_ATCMD_LEN];
    LPSTR szPriceWalk = szPrice;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) && !(lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) &&
        (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) ||
         !(lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY))) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Add "AT"
    (void)strncpyz(szWalk, "AT", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    TBD_ASSERT(NULL != szWalk);

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) {
        // Add "+CAOC="
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CACM=");
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        // Add "<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
    }

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

        // Add "+CAMM=<acm_max>"
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CAMM=%u", lpCostInfo->dwMaxACM);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
    }

    if ((lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) &&
        (lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY)) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

        // Add "+CPUC=<currency>,<ppu>"
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CPUC=\"%s\",\"%u.%.4u\"",
                         USAsciiString(lpCostInfo->wszCurrency),
                         (lpCostInfo->dwCostPerUnit>>16),((lpCostInfo->dwCostPerUnit & 0xFFFF) * 10000)>>16);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        TBD_ASSERT(NULL != szWalk);

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            TBD_ASSERT(NULL != szWalk);
        }
    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmdCMET(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE|CMDOPT_CPIN2RELATED, APIID_SETCOSTINFO, NULL, NULL, hr, (IS_ADI && lpszPassword!=NULL) ? g_CMET_CPUC : NULL)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

UINT g_nRSSI = 28;
UINT g_nBER = 0;
volatile UINT g_nFakeCSQCount = 0;

void FillRSQStruct(RILSIGNALQUALITY* prsq)
{
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    // Calculate signal strength
    prsq->nMinSignalStrength = -112;
    prsq->nMaxSignalStrength = -51;

    prsq->nLowSignalStrength = -110;
    prsq->nHighSignalStrength = -60;

    if (99 == g_iLastRSSI) {
        prsq->nSignalStrength = RIL_SIGNALSTRENGTH_UNKNOWN;
    } else {
        prsq->nSignalStrength = (15 * g_iLastRSSI) + prsq->nMinSignalStrength;
    }
    prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

    // Set bit error rate
    if (NUM_BITERRORRATES > g_iLastSignal) {
        prsq->dwBitErrorRate = g_rgdwBitErrorRates[g_iLastSignal];
    } else {
        prsq->dwBitErrorRate = RIL_BITERRORRATE_UNKNOWN;
    }
    prsq->dwParams |= RIL_PARAM_SQ_BITERRORRATE;

    return;
}


//
//
//
HRESULT ParseGetSignalQuality(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    //TBD_FUNCTION(ParseGetSignalQuality);
    UINT nRSSI;
    UINT nBER;
    RILSIGNALQUALITY* prsq = NULL;
    HRESULT hr = S_OK;

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    LPCSTR szOrigRsp=szRsp;
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

    pBlob = NULL;
    cbBlob = 0;

    prsq = (RILSIGNALQUALITY*)AllocBlob(sizeof(RILSIGNALQUALITY));
    if (!prsq) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    if (g_bCINDIsActive)
    {
        FillRSQStruct(prsq);
    }
    else
    {
        if(g_nFakeCSQCount > 0)
        {
            nRSSI = g_nRSSI;
            nBER = g_nBER;
            g_nFakeCSQCount--;
        }
        else
        {
            // Parse "<prefix>+CSQ: <rssi>,<ber><postfix>"
            if (!ParseRspPrefix(szRsp, szRsp)                            ||
                !MatchStringBeginning(szRsp, "+CSQ: ", szRsp)            ||
                !ParseUIntAndVerifyAbove(szRsp, TRUE, 100, nRSSI, szRsp) ||
                !MatchStringBeginning(szRsp, ",", szRsp)                 ||
                !ParseUInt(szRsp, TRUE, nBER, szRsp)                     ||
                !ParseRspPostfix(szRsp, szRsp)) {
                    hr = E_FAIL;
                    goto Error;
            }
            g_nRSSI = nRSSI;
            g_nBER = nBER;
        }

        // Calculate signal strength
        prsq->nMinSignalStrength = -113;
        prsq->nMaxSignalStrength = -51;

        prsq->nLowSignalStrength = -110;
        prsq->nHighSignalStrength = -60;

        if (99 == nRSSI) {
            prsq->nSignalStrength = RIL_SIGNALSTRENGTH_UNKNOWN;
        } else {
            TBD_ASSERT(32 > nRSSI);
            prsq->nSignalStrength = (2 * nRSSI) + prsq->nMinSignalStrength;
        }
        prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

        // Set bit error rate
        if (NUM_BITERRORRATES > nBER) {
            prsq->dwBitErrorRate = g_rgdwBitErrorRates[nBER];
        } else {
            prsq->dwBitErrorRate = RIL_BITERRORRATE_UNKNOWN;
        }
        prsq->dwParams |= RIL_PARAM_SQ_BITERRORRATE;
    }
    pBlob = (void*)prsq;
    cbBlob = sizeof(RILSIGNALQUALITY);

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    strncpyz(gszGetSignalQualityLast, szOrigRsp, sizeof(gszGetSignalQualityLast));
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH


Error:
    if (FAILED(hr)) {
        FreeBlob(prsq);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetSignalQuality(DWORD dwParam)
{
    //TBD_FUNCTION(RILDrv_GetSignalQuality);
    CCommand* pCmd = NULL;
	CResponse *pRsp = NULL;
	
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_SINGLE_PORT


    if (pHandle->GetDevice()->GetComDevice(DATA_PORT)->FDataMode())
    {
#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
        
        pCmd = new CCommand;
        if (!pCmd)
        {
            RILRetailTrace((TEXT("RilDrv: RILDrv_GetSignalQuality failed in new CCommand\r\n")));
            goto Error;
        }

        if (!pCmd->Init(pHandle, NULL, NULL, CMDOPT_NOOP, 0, NULL, ParseGetSignalQuality, 0,  0, APIID_GETSIGNALQUALITY, NULL, NULL, NULL, NULL))
        {
            RILRetailTrace((TEXT("RilDrv: RILDrv_GetSignalQuality failed in pCmd->Init\r\n")));
            delete pCmd;
            pCmd = NULL;
            goto Error;
        }
        

        // Allocate a new response
        pRsp = new CResponse(DATA_PORT, pHandle->GetDevice());
        if (!pRsp)
        {
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            RILRetailTrace((TEXT("RilDrv: RILDrv_GetSignalQuality failed in Get(pCmd, 0)\r\n")));
            delete pCmd;
            pCmd = NULL;
            goto Error;
        }

        // Create an OK response (since the command was a no-op, it can't fail)
        char *szRemainder;
        UINT cbRemainder;
        if (!pRsp->AppendString(gszGetSignalQualityLast, strlen(gszGetSignalQualityLast), szRemainder, cbRemainder))
        {
            RILRetailTrace((TEXT("RilDrv: RILDrv_GetSignalQuality failed in AppendString\r\n")));
            delete pCmd;
            pCmd = NULL;
            delete pRsp;
            pRsp = NULL;

            goto Error;
        }

        BOOL fHungUp;
        if (!pHandle->GetDevice()->HandleRsp(DATA_PORT, pCmd, pRsp, fHungUp, g_bRadioOff))
        {
            RILRetailTrace((TEXT("RilDrv: RILDrv_GetSignalQuality failed in HandleRsp\r\n")));
            delete pCmd;
            pCmd = NULL;
            delete pRsp;
            pRsp = NULL;

            goto Error;

        }
#endif
    }
    else
    {
        if (!QueueCmd(URC_PORT, pHandle, "AT+CSQ\r", CMDOPT_POLLING|CMDOPT_CPIN2NEUTRAL, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    
#else

    if (!QueueCmd(URC_PORT, pHandle, "AT+CSQ\r", CMDOPT_POLLING|CMDOPT_CPIN2NEUTRAL, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

#endif



Error:

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH

    if (pCmd)
	{
        delete pCmd;
        pCmd = NULL;
	}

    if (pCmd)
	{
        delete pRsp;
        pRsp = NULL;
	}

#endif

    return hr;
}


//
//
//
static HRESULT ParseGetCellTowerInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCellTowerInfo);
    UINT nValue;
    RILCELLTOWERINFO* prcti = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcti = (RILCELLTOWERINFO*)AllocBlob(sizeof(RILCELLTOWERINFO));
    if (!prcti) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcti, 0x00, sizeof(RILCELLTOWERINFO));
    prcti->cbSize = sizeof(RILCELLTOWERINFO);

    // Parse "<prefix>^SMOND: "
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SMOND:", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<mcc>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwMobileCountryCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<mnc>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwMobileNetworkCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_MOBILENETWORKCODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<lac>"
    if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwLocationAreaCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<ci>"
    if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwCellID = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_CELLID;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<bsic>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwBaseStationID = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_BASESTATIONID;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<bcch>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwBroadcastControlChannel = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_BROADCASTCONTROLCHANNEL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevel = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVEL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev_full>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevelFull = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVELFULL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev_sub>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevelSub = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVELSUB;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQuality = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITY;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual_full>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQualityFull = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITYFULL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual_sub>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQualitySub = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITYSUB;
    }

    pBlob = (void*)prcti;
    cbBlob = sizeof(RILCELLTOWERINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcti);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCellTowerInfo(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetCellTowerInfo);

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if(g_dwModemType == MODEMTYPE_SIEMENS_MC45 || g_dwModemType == MODEMTYPE_SIEMENS_MC46)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
    if (!QueueCmd(COMMAND_PORT, pHandle, "AT^SMOND\r", CMDOPT_CPIN2NEUTRAL, APIID_GETCELLTOWERINFO, ParseGetCellTowerInfo,
                  NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

HRESULT ParseGetModemType(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetModemType);
    DWORD *pdwModemType;

    pBlob = NULL;
    cbBlob = 0;

    pdwModemType = (DWORD *)AllocBlob(sizeof(DWORD));
    if( NULL == pdwModemType)
    {
        ASSERT( FALSE );
        goto Error;
    }

    *pdwModemType = g_dwModemType;

    pBlob = pdwModemType;
    cbBlob = sizeof(DWORD);

    return S_OK;

Error:
    FreeBlob(pdwModemType);
    return E_FAIL;
}

HRESULT ParseGetVersionInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetVersionInfo);
    PRADIO_VERSION_INFO prviVersionInfo;
    DWORD dwRHAVersion;

    pBlob = NULL;
    cbBlob = 0;

    prviVersionInfo = (PRADIO_VERSION_INFO)AllocBlob(sizeof(RADIO_VERSION_INFO));
    if( NULL == prviVersionInfo)
    {
        ASSERT( FALSE );
        goto Error;
    }

    RHAFunctionList.pfnRHA_GetRHAVersion(&dwRHAVersion);

    prviVersionInfo->dwStructVersion = RADIO_VERSION_STRUCT_VERSION;
    prviVersionInfo->dwRILVersion = RIL_VERSION_NUM;
    prviVersionInfo->dwRHAVersion = dwRHAVersion;
    prviVersionInfo->dwAudVersion = g_dwRadioAudioProfilesVersion;
    prviVersionInfo->dwMuxVersion = GetMuxVersion();

    pBlob = prviVersionInfo;
    cbBlob = sizeof(RADIO_VERSION_INFO);

    return S_OK;

Error:
    FreeBlob(prviVersionInfo);
    return E_FAIL;
}

#if defined (IOCTL_RIL_GETANTENNASTATUS)
//
//
//
static HRESULT ParseAntennaStatusResponse(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseAntennaDiagResponse);
    UINT nValue;
    UINT *puiDiag;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    puiDiag = (UINT*)AllocBlob(sizeof(UINT));
    if (!puiDiag) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(puiDiag, 0x00, sizeof(UINT));

    // Parse "<prefix>^SAD: <diag><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                           ||
        !MatchStringBeginning(szRsp, "^SAD: ", szRsp)          ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_ANTENNASTATUS > nValue) {
        *puiDiag = g_rgdwAntennaStatus[nValue];
    } else {
        *puiDiag = RIL_ANTENNASTATUS_UNKNOWN;
    }

    pBlob = (void*)puiDiag;
    cbBlob = sizeof(UINT);

Error:
    if (FAILED(hr)) {
        FreeBlob(puiDiag);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetAntennaStatus(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetSignalQuality);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // This function applies to the AC45 only.
    if (g_dwModemType != MODEMTYPE_SIEMENS_AC45)
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (!QueueCmd(COMMAND_PORT, pHandle, "AT^SAD\r", CMDOPT_NONE, APIID_NONE, ParseAntennaStatusResponse, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }
Error:
    return hr;
}
#endif

//
//
//
HRESULT ParseATCResponse(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseATCResponse);
    UINT nLen;
    LPSTR pszRsp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    nLen = strlen(szRsp);

    pszRsp = (LPSTR)AllocBlob(nLen);
    if (!pszRsp) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pszRsp, 0x00, nLen);
    strncpy(pszRsp, szRsp, nLen);

    ParseRspPostfix(szRsp, szRsp);

    pBlob = (void*)pszRsp;
    cbBlob = nLen;

Error:
    if (FAILED(hr)) {
        FreeBlob(pszRsp);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_DevSpecific(
    DWORD dwParam,
    const BYTE* lpbParams,              // @parm parameters for the operation to be performed
    DWORD dwSize                        // @parm size of the data pointed to by <p lpParams> in bytes
)
{
    TBD_FUNCTION(RILDrv_DevSpecific);

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    TBD_ASSERT(NULL != lpbParams);
    TBD_ASSERT(dwSize > 0);


    if (dwSize < sizeof(DWORD))
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    switch ( *(DWORD*)lpbParams )
    {
        case RIL_DEVSPECIFIC_BANDHANDOVER:
        {
            DWORD dwBand, dwHandover;
            char szCmd[MAX_ATCMD_LEN];

            // This function applies to US-band modules only.
            if ((g_dwModemType != MODEMTYPE_SIEMENS_MC46) && (g_dwModemType != MODEMTYPE_SIEMENS_MC56))
            {
                hr = E_NOTIMPL;
                goto Error;
            }
            // Fetch the registry settings
            dwBand = 0;
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRILBand, TEXT("850"), &dwBand);
            dwHandover = 0;
            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRILHandover, TEXT("Handover"), &dwHandover);

            sprintf(szCmd,"AT^SCFG=\"Radio/Band/850\",\"%s\";^SCFG=\"Radio/Band/Handover\",\"%s\"\r",(dwBand==1?"0031":"0030"),(dwHandover==1?"0031":"0030"));

            // Send the band and handover command
            if(!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_DEVSPECIFIC, NULL, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GETMODEMTYPE:
        {
            // Send a noop command to pass the modem type back
            if(!QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetModemType, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GETVERSIONINFO:
        {
            // Send a noop command to pass the modem type back
            if(!QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetVersionInfo, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_SEND_ATC:
        {
            char szCmd[MAX_ATCMD_LEN];

            if (dwSize > MAX_ATCMD_LEN)
            {
                hr = E_FAIL;
                goto Error;
            }

            memset(szCmd, 0, MAX_ATCMD_LEN);
            strncpy(szCmd, (char *)lpbParams+sizeof(DWORD), MAX_ATCMD_LEN);

            // Like in C++ '//' indicates a comment. So we don't execute it and just store it.
            if (strncmp(szCmd, "//", 2) == 0)
            {
                strncpy(g_szSequenceFromDialer, szCmd+2, MAX_ATCMD_LEN-2);

                // Send a noop command
                if(!QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, NULL, NULL, hr))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
            else
            {
                // Send a AT command to the module and pass the result back without any actual parsing.
                if(!QueueCmd(COMMAND_PORT, pHandle, szCmd, CMDOPT_NONE, APIID_DEVSPECIFIC, ParseATCResponse, NULL, hr))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
        }
        break;

        case RIL_DEVSPECIFIC_FORCEEQSTATEOFF:
            // We have to generate an asynchronos response by sending an no operation command.
            if (g_PowerManager.ForcePowerOff())
            {
                QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
            }
            else
                hr = E_FAIL;
        break;

#ifndef RIL_ENABLE_POWER_HANDLING
        case RIL_DEVSPECIFIC_DISABLEURCS:
        {
            // Disable the URCs
            g_PowerManager.EnterUrcState(pHandle, DEFAULT_URCCONFIG);
            QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
        }
        break;

        case RIL_DEVSPECIFIC_ENABLEURCS:
        {
            // Enable the URCs
            g_PowerManager.EnterUrcState(pHandle, MODEMSTATE_SIEMENS_POWERON);
            QueueCmd(COMMAND_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
        }
        break;
#endif // RIL_ENABLE_POWER_HANDLING

        default:
        {
            hr = E_NOTIMPL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
static HRESULT ParseGetCurrentSystemType(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    TBD_FUNCTION(ParseGetCurrentSystemType);
    DWORD* pdwSystemType = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwSystemType = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwSystemType)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwSystemType, 0x00, sizeof(DWORD));

#ifdef SIEMENS_MAGNETO
#if defined(RIL_SYSTEMTYPE_EDGE)
    if (g_dwModemType == MODEMTYPE_SIEMENS_MC75)
        *pdwSystemType = RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS | RIL_SYSTEMTYPE_EDGE;
    else
#endif
        *pdwSystemType = RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS;
#else
    *pdwSystemType = RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS;
#endif

    pBlob = (void*)pdwSystemType;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwSystemType);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCurrentSystemType(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_GetCurrentSystemType);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (!QueueCmd(URC_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_GETCURRENTSYSTEMTYPE, ParseGetCurrentSystemType, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
BOOL CRilHandle::ExParseCGMMResponse(char *pszATResponse, UINT uiBufLen)
{
    DWORD dwStartPos, dwEndPos;
    UINT iPos = 0;

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: +ExParseCGMMResponse()\r\n"));

    dwStartPos = 0;
    g_dwModemType = MODEMTYPE_UNKNOWN;
    while (g_SupportedModems[iPos].szModem != NULL)
    {
        if(FindString(dwStartPos, pszATResponse, g_SupportedModems[iPos].szModem, uiBufLen, &dwEndPos))
        {
            RILRetailTrace((TEXT("RilDrv: Modem is an %a\r\n"), g_SupportedModems[iPos].szModem));
            g_dwModemType = g_SupportedModems[iPos].dwType;
            break;
        }
        iPos++;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: -ExParseCGMMResponse()"));

    return (g_dwModemType == MODEMTYPE_UNKNOWN ? FALSE : TRUE);
}

BOOL ExParseCGMRResponse(char *pszATResponse, UINT uiBufLen)
{
    char *ptr;
    int  iVerMin;
    int  iVerMaj;

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: +ExParseCGMRResponse()\r\n"));

    ptr = strstr(pszATResponse, "REVISION");
    if (ptr != NULL && strlen(ptr)>11)
    {
        iVerMaj = atoi(ptr + 9);
        ptr = strstr(ptr, ".");

        if (ptr != NULL && strlen(ptr)>1)
            iVerMin = atoi(ptr + 1);
        else
            iVerMin = 0;

        g_dwModemVersion = (iVerMaj * 1000) + iVerMin;
        RILRetailTrace((TEXT("RilDrv: Modem firmware version is %u.%u\r\n"), iVerMaj, iVerMin));
    }
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: -ExParseCGMRResponse()"));
    return TRUE;
}

//
//
//
BOOL CRilHandle::CheckModemType(COM_PORT_TYPE PortType)
{
    const char szModemTypeCmd[] = "AT+CGMM\r";
    const char szModemVerCmd[] = "AT+CGMR\r";
    char szResponse[64];
#ifdef TBD_DEBUG
    WCHAR wszModemType[64];
#endif
    BOOL fRet = FALSE;

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: +CheckModemType()"));

    if (!ExSendATCmdGetRsp(PortType, (char*)szModemTypeCmd, szResponse, sizeof(szResponse), 10000, 1))
    {
        goto Error;
    }
    if(!ExParseCGMMResponse(szResponse, sizeof(szResponse)))
    {
        RILRetailTrace((TEXT("RilDrv: Unable to get modem type\r\n")));
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("Unable to get modem type from radio"));
        goto Error;
    }

    if (!ExSendATCmdGetRsp(PortType, (char*)szModemVerCmd, szResponse, sizeof(szResponse), 10000, 1))
    {
        goto Error;
    }
    if(!ExParseCGMRResponse(szResponse, sizeof(szResponse)))
    {
        RILRetailTrace((TEXT("RilDrv: Unable to get modem version\r\n")));
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("Unable to get modem version from radio"));
        goto Error;
    }

#ifdef TBD_DEBUG
    switch(g_dwModemType)
    {
    case MODEMTYPE_SIEMENS_MC45:
        wcscpy(wszModemType, TEXT("Siemens MC45"));
        break;
    case MODEMTYPE_SIEMENS_MC46:
        wcscpy(wszModemType, TEXT("Siemens MC46"));
        break;
    case MODEMTYPE_SIEMENS_MC55:
        wcscpy(wszModemType, TEXT("Siemens MC55/XT55"));
        break;
    case MODEMTYPE_SIEMENS_MC56:
        wcscpy(wszModemType, TEXT("Siemens MC56/XT56"));
        break;
    case MODEMTYPE_SIEMENS_MC75:
        wcscpy(wszModemType, TEXT("Siemens MC75"));
        break;
    case MODEMTYPE_SIEMENS_TC63:
        wcscpy(wszModemType, TEXT("Siemens TC63"));
        break;
    case MODEMTYPE_SIEMENS_AC45:
        wcscpy(wszModemType, TEXT("Siemens AC45"));
        break;
    }
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("Radio modem type = %s"), wszModemType);
#endif
    fRet = TRUE;

Error:
    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: -CheckModemType()"));

    return fRet;
}

//
//
//
BOOL CRilHandle::BandHandover(COM_PORT_TYPE PortType)
{
    DWORD dwBand, dwHandover;
    char szBand[] = "AT^SCFG=\"Radio/Band/850\",\"0\"\r";
    char szHandover[] = "AT^SCFG=\"Radio/Band/Handover\",\"0\"\r";
    char szResponse[64];

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: +BandHandover()"));
    // Fetch the registry settings
    dwBand = 0;
    GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRILBand, TEXT("850"), &dwBand);
    dwHandover = 0;
    GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRILHandover, TEXT("Handover"), &dwHandover);

    if(dwBand==1)szBand[26] = '1';
    else szBand[26] = '0';

    if (!ExSendATCmdGetRsp(PortType, (char*)szBand, szResponse, sizeof(szResponse), 120000, 1)) {
      return FALSE;
    }

    if(dwHandover==1)szHandover[31] = '1';
    else szHandover[31] = '0';

    if (!ExSendATCmdGetRsp(PortType, (char*)szHandover, szResponse, sizeof(szResponse), 120000, 1)) {
      return FALSE;
    }

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: -BandHandover()"));

    return TRUE;
}

//
//
//
BOOL CRilHandle::InitMicrophone(COM_PORT_TYPE PortType)
{
    DWORD dwTimeout;
    const char szCmd[] = "AT^SNFM=,1\r";
    char szResponse[64];

    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: +InitMicrophone()"));
    // Fetch the registry settings
    dwTimeout = GetSNFMTimeout();

    if (!ExSendATCmdGetRsp(PortType, (char*)szCmd, szResponse, sizeof(szResponse), dwTimeout, 0)) {
      return FALSE;
    }


    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("RilDrv: -InitMicrophone()"));

    return TRUE;
}


